﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Smarly.Bussines.Membership;
using Smarly.Bussines.Parsing;
using Smarly.Core;
using Smarly.Core.Database;

namespace Smarly.Bussines.Model
{
	public class BookTree : ModelStateObject
	{

		public long Id { get; set; }

		internal long ParentId { get; set; }
		public BookTree Parent { get; set; }
		public List<BookTree> Children { get; set; }

		internal Guid BookId { get; set; }
		public Book Book { get; set; }

		internal int LeftKey { get; set; }

		public byte Level { get; set; }

		[Required(ErrorMessage = "Поле не заполнено")]
		[DatabaseValidation("HashTagExist", "Такой хэш тэг уже используется")]
		public string Url { get; set; }
		public bool IsHead { get; set; }

		[Required(ErrorMessage = "Поле не заполнено")]
		public string Text { get; set; }

		public bool Published { get; set; }

		public string HashTag { get; set; }

		public DateTime UpdateDate { get; set; }

/*
		public static void RefreshCache()
		{
			SqlRepository.ExecuteProcedureReader<BookTree>("Text_BookTree_Cache_Refresh",
																					 new List<SpParam>
			                                     	{
			                                     	}, null,
																					 (reader) => null);
		}
*/


		public BookTree Get(SmarlyIdentity identity, string url)
		{
			Url = url;
			return SqlRepository.ExecuteProcedureReader("Text_BookTree_Get",
																					 new List<SpParam>
			                                     	{
			                                     		new SpParam {Name = "@userId", Value = identity.UserId, Type = DbType.Guid},
			                                     		new SpParam {Name = "@url", Value = url, Type = DbType.String},
			                                     	}, null,
																					 ReadGet);

		}

		public BookTree Get(string url)
		{
			return SqlRepository.ExecuteProcedureReader("Text_BookTree_Element",
																					 new List<SpParam>
			                                     	{
			                                     		new SpParam {Name = "@bookTreeId", Value = Id, Type = DbType.Int64},
			                                     		new SpParam {Name = "@url", Value = url, Type = DbType.String},
			                                     	}, OnSqlInfoMessage,
			                                            (reader) =>
				                                            {
					                                            while (reader.Read())
					                                            {
						                                            Text = reader.GetString("Text");
					                                            }
					                                            return this;
				                                            });
		}

		private BookTree ReadGet(IDataReader reader)
		{
			Book book = null;
			BookTree result = null;
			int batchNum = 0;
			do
			{
				switch (batchNum)
				{
					case 0:
						while (reader.Read())
						{
							book = new Book
								       {
									       Author = reader.GetString("Author"),
												 CoverUrl = reader.GetString("CoverUrl"),
												 ExistPdf = reader.GetBoolean("ExistPdf"),
								       };
							
						}
						break;
					case 1:
						List<BookTree> list = new List<BookTree>();
						while (reader.Read())
						{
							var item = new BookTree
								           {
									           Id = reader.GetInt64("BookTreeId"),
									           ParentId = reader.GetInt64OrNull("ParentBookTreeId") ?? -1,
									           LeftKey = reader.GetInt32("LeftKey"),
									           Level = reader.GetByte("Level"),
									           IsHead = reader.GetBoolean("IsHead"),
									           Text = reader.GetString("Text"),
									           Url = reader.GetStringOrNull("Url"),
														 Published = reader.GetBoolean("Published"),
														 HashTag = reader.GetStringOrNull("HashTag"),
														 Book = book,
								           };
							list.Add(item);

							if (item.Url == Url)
								result = item;
						}

						BookTree root = list.Single(x => x.ParentId == -1);
						CreateTree(root, list);

						book.Root = root;

						break;
					default:
						throw new ApplicationException("Additional batch select");

				}
				batchNum++;
			} while (reader.NextResult());

			return result;
		}

		public static void CreateTree(BookTree root, List<BookTree> list)
		{
			if (root == null || list == null)
				return;

			root.Children = list.Where(x => x.ParentId == root.Id).OrderBy(x => x.LeftKey).ToList();

			if (root.Children != null)
				foreach (var child in root.Children)
				{
					child.Parent = root;
					CreateTree(child, list);
				}
		}


		public void SaveHead(string current, string after)
		{
			SqlRepository.ExecuteProcedureReader<BookTree>("Text_BookTree_AddHead",
																					 new List<SpParam>
																					 {
			                                     		new SpParam {Name = "@bookTreeId", Value = (Id > 0 ? (int?)Id : null), Type = DbType.Int64},
			                                     		new SpParam {Name = "@current", Value = current, Type = DbType.String},
			                                     		new SpParam {Name = "@after", Value = after ?? string.Empty, Type = DbType.String},
			                                     		new SpParam {Name = "@hashTag", Value = Url, Type = DbType.String},
			                                     		new SpParam {Name = "@text", Value = Text, Type = DbType.String},
																					 }, OnSqlInfoMessage,
																					 (reader) => null);
		}

		public void UpdateHead()
		{
			SqlRepository.ExecuteProcedureReader<BookTree>("Text_BookTree_UpdateHead",
																					 new List<SpParam>
																					 {
			                                     		new SpParam {Name = "@bookTreeId", Value = Id, Type = DbType.Int64},
			                                     		new SpParam {Name = "@hashTag", Value = Url, Type = DbType.String},
			                                     		new SpParam {Name = "@text", Value = Text, Type = DbType.String},
																					 }, OnSqlInfoMessage,
																					 (reader) => null);
		}

		public BookTree SaveText(long after, string parentTag)
		{
			return SqlRepository.ExecuteProcedureReader<BookTree>("Text_BookTree_AddText",
																					 new List<SpParam>
																					 {
			                                     		new SpParam {Name = "@current", Value = Id, Type = DbType.Int64},
			                                     		new SpParam {Name = "@after", Value = after, Type = DbType.Int64},
			                                     		new SpParam {Name = "@text", Value = Text, Type = DbType.String},
			                                     		new SpParam {Name = "@parentHashTag", Value = parentTag, Type = DbType.String},
																					 }, OnSqlInfoMessage,
			                                               (reader) =>
				                                               {
					                                               while (reader.Read())
					                                               {
						                                               Id = reader.GetInt64("BookTreeId");
					                                               }
					                                               return this;
				                                               });
		}

		public void Delete(string hashTag)
		{
			SqlRepository.ExecuteProcedureReader<BookTree>("Text_BookTree_Delete",
																					 new List<SpParam>
																					 {
			                                     		new SpParam {Name = "@bookTreeId", Value = Id, Type = DbType.Int64},
			                                     		new SpParam {Name = "@url", Value = hashTag, Type = DbType.String},
																					 }, OnSqlInfoMessage,
																						(reader) => this);
		}

		public static List<BookTree> GetFullTree()
		{
			return SqlRepository.ExecuteProcedureReader("Text_BookTree_GetHeaders",
			                                            new List<SpParam> {},
			                                            null,
			                                            (reader) =>
				                                            {
					                                            List<BookTree> result = new List<BookTree>();
					                                            while (reader.Read())
					                                            {
						                                            result.Add(new BookTree
							                                                       {
																																			 Text = reader.GetString("Text"),
								                                                       Url = reader.GetString("Url"),
																																			 UpdateDate = reader.GetDateTime("UpdateDate"),
							                                                       });
					                                            }
					                                            return result;
				                                            });
		}

		public BookTree ChangePublishStatus(string currentUrl)
		{
			return SqlRepository.ExecuteProcedureReader("Text_BookTree_ChangePublishStatus",
			                                            new List<SpParam>
				                                            {
					                                            new SpParam {Name = "@url", Value = currentUrl, Type = DbType.String},
				                                            },
			                                            null,
			                                            (reader) => this
				);
		}

		#region subscribe

		public static NewsModel GetNews(DateTime? from = null, DateTime? till = null)
		{
			return SqlRepository.ExecuteProcedureReader("Text_BookTree_News",
			                                            new List<SpParam>
				                                            {
					                                            new SpParam {Name = "@from", Value = from, Type = DbType.String},
					                                            new SpParam {Name = "@till", Value = till, Type = DbType.String},
				                                            },
			                                            null,
																									ReadRssNews);
		}

/*
		private static NewsModel ReadNews(IDataReader reader)
		{
			Dictionary<BookTree, List<BookTree>> result = new Dictionary<BookTree, List<BookTree>>();
			List<string> emails = new List<string>();

			List<Book> parents = new List<Book>();

			List<BookTree> books = new List<BookTree>();
			List<BookTree> sections = new List<BookTree>();
			int batchNum = 0;
			do
			{
				switch (batchNum)
				{
					case 0:
						while (reader.Read())
						{
							Book book = new Book
								            {
									            BookId = reader.GetGuid("BookId"),
									            CoverUrl = reader.GetString("CoverUrl"),
									            Author = reader.GetString("Author"),
								            };
							parents.Add(book);
						}
						break;
					case 1:
						while (reader.Read())
						{
							BookTree book = new BookTree
								                {
									                Text = reader.GetString("Text"),
									                BookId = reader.GetGuid("BookId"),
									                Url = reader.GetStringOrNull("Url"),
								                };
							books.Add(book);
						}
						break;
					case 2:
						while (reader.Read())
						{
							var item = new BookTree
								           {
									           Text = reader.GetString("Text"),
									           BookId = reader.GetGuid("BookId"),
									           Url = reader.GetStringOrNull("Url"),
								           };
							sections.Add(item);
						}

						foreach (var currentBook in books)
						{

							var l = sections
								.Where(x => x.BookId == currentBook.BookId)
								.Select(x =>
									        {
										        x.Parent = currentBook;
										        x.Book = parents.Single(b => b.BookId == currentBook.BookId);
										        return x;
									        })
								.ToList();

							if (l.Count > 0)
								result.Add(currentBook, l);
						}

						break;
					case 3:
						while (reader.Read())
						{
							emails.Add(reader.GetString("Email"));
						}
						break;
					default:
						throw new ApplicationException("Additional batch select");

				}
				batchNum++;
			} while (reader.NextResult());

			return new NewsModel {News = result, Subscribers = emails, Sections = sections};
		}
*/

		#endregion

		#region rss

/*
		public static NewsModel GetRssNews()
		{
			return SqlRepository.ExecuteProcedureReader("Text_BookTree_RssNews",
			                                            new List<SpParam>
				                                            {
				                                            },
			                                            null,
			                                            ReadRssNews);
		}
*/

		private static NewsModel ReadRssNews(IDataReader reader)
		{
			Dictionary<BookTree, List<BookTree>> result = new Dictionary<BookTree, List<BookTree>>();
			List<string> emails = new List<string>();

			List<Book> parents = new List<Book>();

			List<BookTree> books = new List<BookTree>();
			List<BookTree> sections = new List<BookTree>();
			int batchNum = 0;
			do
			{
				switch (batchNum)
				{
					case 0:
						while (reader.Read())
						{
							Book book = new Book
								            {
									            BookId = reader.GetGuid("BookId"),
									            CoverUrl = reader.GetString("CoverUrl"),
									            Author = reader.GetString("Author"),
								            };
							parents.Add(book);
						}
						break;
					case 1:
						while (reader.Read())
						{
							BookTree book = new BookTree
								                {
									                Text = reader.GetString("Text"),
									                BookId = reader.GetGuid("BookId"),
									                Url = reader.GetStringOrNull("Url"),
								                };
							books.Add(book);
						}
						break;
					case 2:
						while (reader.Read())
						{
							var item = new BookTree
								           {
														 Id = reader.GetInt64("BookTreeId"),
									           Text = reader.GetString("Text"),
									           BookId = reader.GetGuid("BookId"),
									           Url = reader.GetString("Url"),
														 UpdateDate = reader.GetDateTime("PublishedDate")
								           };
							sections.Add(item);
						}

						foreach (var currentBook in books)
						{

							var l = sections
								.Where(x => x.BookId == currentBook.BookId)
								.Select(x =>
									        {
										        x.Parent = currentBook;
										        x.Book = parents.Single(b => b.BookId == currentBook.BookId);
														x.Children = new List<BookTree>();
										        return x;
									        })
								.ToList();

							if (l.Count > 0)
								result.Add(currentBook, l);
						}

						break;
					case 3:
						while (reader.Read())
						{
							long parentId = reader.GetInt64("ParentBookTreeId");
							var ch = sections.Single(x => x.Id == parentId);

							var item = new BookTree
								           {
														 Id = parentId,
									           Text = reader.GetString("Text"),
									           BookId = reader.GetGuid("BookId"),
									           Url = reader.GetString("Url"),
														 UpdateDate = reader.GetDateTime("PublishedDate"),
														 LeftKey = reader.GetInt32("LeftKey"),
								           };
							ch.Children.Add(item);
						}

						foreach (var bookTree in sections)
						{
							bookTree.Children = bookTree.Children.OrderBy(x => x.LeftKey).ToList();
						}
						break;
					case 4:
						while (reader.Read())
						{
							emails.Add(reader.GetString("Email"));
						}
						break;

					default:
						throw new ApplicationException("Additional batch select");

				}
				batchNum++;
			} while (reader.NextResult());

			return new NewsModel {News = result, Subscribers = emails, Sections = sections};
		}

		#endregion


	}

	public class NewsModel
	{
		public Dictionary<BookTree, List<BookTree>> News { get; set; }
		public List<string> Subscribers { get; set; }
		public List<BookTree> Sections { get; set; }
	}

	public static class BookTreeExtentions
	{
		public static void Save(this List<BookTree> texts, long after, string parentTag)
		{
			foreach (var text in texts)
			{
				text.SaveText(after, parentTag);
				after = text.Id;
			}
		}

		public static List<BookTree> ToText(this string bbCode)
		{
			List<BookTree> result = new List<BookTree>();

			if (string.IsNullOrWhiteSpace(bbCode))
			{
				return new List<BookTree>(0);
			}

			string str = Regex.Replace(bbCode, @"\[pre=(.)+?](.|\s)+?\[/pre]", (m) =>
			{
				string value = m.Value;
				value = value.Replace("[br]", "\r\n");
				value = value.Replace("[i]", "&#91;i]");
				return value;
			}).ToHtml();

			str = Regex.Replace(str, @"\<pre(.|\s)+?\</pre>", (m) => m.Value.Replace("&#91;i]", "[i]"));

			if (!str.Trim().StartsWith("<"))
				str = "<p>" + str + "</p>";

			const string htmlElements = "<!DOCTYPE documentElement[" +

																	"<!ENTITY Alpha \"&#913;\">" +
																	"<!ENTITY ndash \"&#8211;\">" +
																	"<!ENTITY mdash \"&#8212;\">" +
																	"<!ENTITY amp \"&#38;\">" +

																	"<!ENTITY sect \"&#167;\">" +
																	"<!ENTITY copy \"&#169;\">" +
																	"<!ENTITY reg \"&#174;\">" +

																	"<!ENTITY euro \"&#8364;\">" +
																	"<!ENTITY pound \"&#163;\">" +
																	"<!ENTITY trade \"&#8482;\">" +
																	"]>";
			XDocument doc = XDocument.Parse(htmlElements + "<root>" + str + "</root>", LoadOptions.PreserveWhitespace);

			foreach (var el in doc.Root.Elements())
			{
				var id = el.ParseElementId();
				el.SetAttributeValue("id", id > 0 ? "text-" + id : "smarly-id-1");
				var value = el.ToString(SaveOptions.DisableFormatting);//( ?? string.Empty).Trim();
				if (string.IsNullOrWhiteSpace(el.InnerXmlValue().Trim()) && id < 0)
				{
					continue;
				}

				result.Add(new BookTree { Id = id, Text = value/*, Tag = (Tag)Enum.Parse(typeof(Tag), el.Name.ToString())*/ });
			}


			return result;

		}

		private static long ParseElementId(this XElement root)
		{
			return long.Parse(ParseAttribute(root, "id").Replace("text-", string.Empty));
		}

		private static string ParseAttribute(XElement root, string name)
		{
			var attribute = root.Attributes(name).SingleOrDefault();
			return attribute != null ? attribute.Value : "-1";
		}

		private static string InnerXmlValue(this XElement root)
		{
			if (root.Value == string.Empty)
				return string.Empty;

			var doc = new XDocument(root).Root;
			doc.Value = ";";
			var array = doc.ToString(SaveOptions.DisableFormatting).Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			string result = root.ToString(SaveOptions.DisableFormatting);
			result = result.Substring(0, result.Length - array[1].Length).Substring(array[0].Length);
			return result;
		}



	}
}
